# -*- coding: utf-8 -*-
import numpy as np
import preprocess.PartitionData as PartitionData
class Stacking:
    
    CutMethods = ['char','parse']
    
    def __init__(self,X_train,Y_train,X_test,Y_test,Methods):
        [self.X_split,self.Y_split] = PartitionData.partition_pair_n_folds(X_train, Y_train, 2 * len(Methods))
        [self.order,self.order_inv] = Stacking.generate_order(Y_train)
        
        self.Train_Second_X = self.Generate_Second_Train(Methods)
        self.Train_Second_X = Stacking.transfer_X(self.Train_Second_X,self.order)
        
        self.Test_Second_X = self.Generate_Second_Test(X_train,Y_train,X_test,Y_test,Methods)
        self.Test_Second_X = Stacking.transfer_X(self.Test_Second_X,self.order)
        
        self.Train_Second_Y = Stacking.transfer_Y(np.array(self.Y_split).reshape(-1),self.order)
        self.Test_Second_Y = Stacking.transfer_Y(Y_test,self.order)


    
    @staticmethod
    def generate_order(X_test):
        #order_inv = []
        order = {}
        uniq = np.unique(X_test)
        cnt = 0
        for u in uniq:
            #order_inv.append(u)
            order[u] = cnt
            cnt += 1
        return order,uniq
    
    def Classifiers(self,X_train,Y_train,X_test,Y_test,Dict_Method_Predictions,Methods):
        
        if len(Dict_Method_Predictions) == 0:
            for method in Methods:
                for c in self.CutMethods:
                    Dict_Method_Predictions[method.NAME+'_'+c] = []
        for method in Methods:
                for c in self.CutMethods:
                    clf = method.Fit(X_train,Y_train,X_test,Y_test,c)
                    Dict_Method_Predictions[method.NAME+'_'+c].extend(clf.predictions)
        return Dict_Method_Predictions
    
    def Dict_to_Set(self,Dict_Method_Predictions,Methods):

        M_Set = []
        for method in Methods:
            for c in self.CutMethods:
                M_Set.append(Dict_Method_Predictions[method.NAME+'_'+c])
        M_Set = np.array(M_Set).T
        return M_Set
    
    @staticmethod
    def transfer_X(Second_X,order):
        row_length = len(order) * len(Second_X[0])
        Transfer_X = np.zeros((len(Second_X),row_length))
        for i in range(len(Second_X)):
            x = Second_X[i]
            for j in range(len(Second_X[0])):
                word = x[j]
                position_y = order[word] + j * len(order)
                Transfer_X[i][position_y] = 1
        return Transfer_X
    
    @staticmethod
    def transfer_Y(Second_Y,order):
        Transfer_Y = [order[i] for i in Second_Y]
        return Transfer_Y

    def Generate_Second_Train(self,Methods):
        Dict_Method_Predictions = {}
        for n in range(len(Methods)*2):
            print ('Fold: %d/%d ========== ' %(n+1,len(Methods)*2))
            Test_X = self.X_split[n]
            Test_Y = self.Y_split[n]
            Train_X = []
            Train_Y = []
            for i in range(len(Methods)*2):
                if i != n:
                    Train_X.extend(self.X_split[i])
                    Train_Y.extend(self.Y_split[i])
            Dict_Method_Predictions = self.Classifiers(Train_X,Train_Y,Test_X,Test_Y,Dict_Method_Predictions,Methods)
        Train_Second_X = self.Dict_to_Set(Dict_Method_Predictions,Methods)
        return Train_Second_X
                                                                 
    def Generate_Second_Test(self,X_train,Y_train,X_test,Y_test,Methods):
        Dict_Method_Predictions = {}
        print ('Last ===============')
        Dict_Method_Predictions = self.Classifiers(X_train,Y_train,X_test,Y_test,Dict_Method_Predictions,Methods)
        Test_Second_X = self.Dict_to_Set(Dict_Method_Predictions,Methods)    
        return Test_Second_X

